// Package eventbroker 对etcd客户端的主要接口进行了统一接口的包装并简化部分调用方式. 部分对象以兼容etcd为标准.
package eventbroker

import (
	"errors"
	"log"
	"time"

	etcdClient "github.com/coreos/etcd/client"
	"golang.org/x/net/context"
)

type Config struct {
	Endpoints []string

	Username string

	Password string
}

type Client struct {
	kapi etcdClient.KeysAPI
	cfg  *Config
}

const DefaultGetTimout = time.Duration(time.Second * 10)

func NewClient(cfg Config) (*Client, error) {
	eCfg := etcdClient.Config{
		Endpoints:               cfg.Endpoints,
		Username:                cfg.Username,
		Password:                cfg.Password,
		HeaderTimeoutPerRequest: DefaultGetTimout,
	}
	c, err := etcdClient.New(eCfg)
	if err != nil {
		return nil, err
	}
	kapi := etcdClient.NewKeysAPI(c)
	client := &Client{
		kapi: kapi,
		cfg:  &cfg,
	}
	return client, nil
}
func (this *Client) EtcdApi() etcdClient.KeysAPI {
	return this.kapi
}

func (this *Client) SetWithTTL(key, value string, ttl time.Duration) error {
	_, err := this.kapi.Set(context.Background(), key, value, &etcdClient.SetOptions{
		TTL: ttl,
	})
	return err
}

func (this *Client) Set(key, value string) error {
	_, err := this.kapi.Set(context.Background(), key, value, nil)
	return err
}

func (this *Client) Delete(key string) error {
	_, err := this.kapi.Delete(context.Background(), key, nil)
	return err
}

// Watch 监听key的修改。
// msgChan key的值
func (this *Client) Watch(key string, msgChan chan WatchResponse, recursive bool) {
	wt := this.kapi.Watcher(key, &etcdClient.WatcherOptions{
		Recursive: recursive,
	})
	go func() {
		for {
			resp, err := wt.Next(context.Background())
			if err != nil {
				log.Println("etcd watch error", err)
				continue
			}
			if resp.Action == "set" || resp.Action == "update" || resp.Action == "compareAndSwap" || resp.Action == "create" || resp.Action == "delete" {
				msgChan <- WatchResponse{
					Key:    resp.Node.Key,
					Value:  resp.Node.Value,
					Action: resp.Action,
				}
			}
		}
	}()
}

// GetNodes (非递归地)获取目录下的key及其value
func (this *Client) GetNodes(key string) (nodes map[string]string, err error) {
	var resp *etcdClient.Response
	resp, err = this.kapi.Get(context.Background(), key, &etcdClient.GetOptions{
		Recursive: true,
	})
	if err != nil {
		return nil, err
	}
	nodes = map[string]string{}
	if !resp.Node.Dir {
		err = errors.New(key + " is not a directory!")
		return
	}
	for _, node := range resp.Node.Nodes {
		if !node.Dir {
			nodes[node.Key] = node.Value
		}
	}
	return
}

func (this *Client) Get(key string) (value string, err error) {
	resp, err := this.kapi.Get(context.Background(), key, nil)
	if err != nil {
		return
	}
	value = resp.Node.Value
	return
}
